﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Office.Interop.Visio;
using System.Windows.Media.Imaging;
using stdole;
using System.IO;
using System.Drawing.Imaging;

using WPF = System.Windows;

namespace TestVisioCanvas.Helpers
{
    public sealed class VisioUtility
    {
        private const double currencyTolerance = 0.1;
        private VisioUtility() { }

        /// <summary>The OpenStencil method opens the specified Visio document stencil.</summary>
        /// <param name="drawingControl">Drawing control with the collection of
        ///  Visio documents to add the new stencil to.</param>
        /// <param name="stencilPath">The stencil path\filename to open.</param>
        /// <returns>Document object if the stencil is opened successfully.
        /// A COMException is thrown if the stencil cannot be opened.</returns>
        [CLSCompliant(false)]
        public static Document OpenStencil(
            AxMicrosoft.Office.Interop.VisOcx.AxDrawingControl drawingControl,
            string stencilPath)
        {

            if (drawingControl == null)
            {
                throw new ArgumentNullException("drawingControl");
            }

            // The drawing control object must be valid.
            if (drawingControl.Window == null || drawingControl.Window.Application == null)
            {
                // Throw a meaningful error.
                throw new ArgumentNullException("drawingControl");
            }
            Document targetStencil = null;

            Documents targetDocuments;
            targetDocuments = (Documents)drawingControl.Window.Application.Documents;

            // Open the stencil file invisibly.
            // A COMException will be thrown if the open fails.
            // If the stencil is currently open Visio will return a 
            // reference to the open stencil.
            targetStencil = targetDocuments.OpenEx(stencilPath,
                (short)VisOpenSaveArgs.visOpenRO |
                (short)VisOpenSaveArgs.visOpenHidden |
                (short)VisOpenSaveArgs.visOpenMinimized |
                (short)VisOpenSaveArgs.visOpenNoWorkspace);

            return (targetStencil);
        }

        /// <summary>The GetMaster method gets the master by name.</summary>
        /// <param name="drawingControl">Drawing control with the collection of
        ///  Visio documents that contain the stencil and masters.</param>
        /// <param name="stencilPath">The stencil path\filename.</param>
        /// <param name="masterNameU">The universal name of the master.</param>
        /// <returns>Master object if found. A COMException is thrown if not found.</returns>
        [CLSCompliant(false)]
        public static Master GetMaster(
            AxMicrosoft.Office.Interop.VisOcx.AxDrawingControl drawingControl,
            string stencilPath,
            string masterNameU)
        {
            // The drawing control object must be valid.
            if (drawingControl == null)
            {
                // Throw a meaningful error.
                throw new ArgumentNullException("drawingControl");
            }

            Master targetMaster = null;
            Document targetDocument;
            Masters targetMasters;

            targetDocument = OpenStencil(drawingControl, stencilPath);
            targetMasters = targetDocument.Masters;
            targetMaster = targetMasters.get_ItemU(masterNameU);

            return (targetMaster);
        }

        /// <summary>
        /// Conversion from Centimeters to inches
        /// </summary>
        /// <param name="cm">The value in CM</param>
        /// <returns>The correspondant value expressed in Inches</returns>
        public static double CmToInch(double cm)
        {
            return MmToInch(cm * 10);
        }

        /// <summary>
        /// Conversion from millimeters to inches
        /// </summary>
        /// <param name="cm">The value in MM</param>
        /// <returns>The correspondant value expressed in Inches</returns>
        public static double MmToInch(double mm)
        {
            return mm * 0.0393700787;
        }

        /// <summary>
        /// Conversion from Inches to Centimeters
        /// </summary>
        /// <param name="cm">The value in Inches</param>
        /// <returns>The correspondant value expressed in Centimeters</returns>
        public static double InchToCm(double inch)
        {
            return inch * 2.54;
        }
        /// <summary>The GetClickedShape method finds a shape at the specified
        /// location.</summary>
        /// <remarks>If there are more than one shape at the location the top
        /// shape in Z order is returned.</remarks>
        /// <param name="drawingControl">Drawing control with the Visio page
        ///  containing the location.</param>
        /// <param name="clickLocationX">The X coordinate of the location in
        /// Visio page units (inches).</param>
        /// <param name="clickLocationY">The Y coordinate of the location in
        /// Visio page units (inches).</param>
        /// <param name="tolerance">The distance from the location to an
        /// included shape.</param>
        /// <returns>The Visio shape at the location or null if no shape is
        /// found.</returns>
        [CLSCompliant(false)]
        public static IVShape GetClickedShape(
            AxMicrosoft.Office.Interop.VisOcx.AxDrawingControl drawingControl,
            double clickLocationX,
            double clickLocationY,
            double tolerance)
        {

            if (drawingControl == null)
                return null;

            // The drawing control object must be valid.
            if (drawingControl.Window == null)
            {
                // do nothing...and nothing can be done as well.
                return null;
            }

            IVShape foundShape = null;

            Page currentPage;

            currentPage = drawingControl.Window.PageAsObj;

            // Use the spatial search method to return a list of
            // shapes at the location.
            Selection foundShapes = currentPage.get_SpatialSearch(
                clickLocationX, clickLocationY,
                (short)VisSpatialRelationCodes.visSpatialContainedIn,
                tolerance,
                (short)VisSpatialRelationFlags.visSpatialFrontToBack);

            if (foundShapes.Count > 0)
            {
                // The selection collection of shapes is 1-based, so
                // the first shape is at index 1.
                foundShape = foundShapes[1];
            }

            return foundShape;
        }

        [CLSCompliant(false)]
        public static IVShape GetClickedShape(
            AxMicrosoft.Office.Interop.VisOcx.AxDrawingControl drawingControl,
            double clickLocationX,
            double clickLocationY)
        {
            return
                GetClickedShape(drawingControl, clickLocationX, clickLocationY, currencyTolerance);
        }


        /// <summary>The MapVisioToWindows method converts Visio coordinates to
        /// Windows coordinates.</summary>
        /// <remarks>The conversion includes changing from Visio's coordinate
        /// system (inches) to the Windows coordinate system (pixels). The
        /// conversion of the Y coordinate must also take into the account the
        /// different locations of the origin, since Visio's origin is at the
        /// lower-left corner and the Windows origin is at the upper-left
        /// corner.</remarks>
        /// <param name="drawingControl">Drawing control with the Visio window
        ///  to use.</param>
        /// <param name="visioX">X position in the Visio coordinate system.
        /// </param>
        /// <param name="visioY">Y position in the Visio coordinate system.
        /// </param>
        /// <returns>Point containing the given Visio coordinate in Windows
        /// coordinates.</returns>
        [CLSCompliant(false)]
        public static System.Drawing.Point MapVisioToWindows(
            AxMicrosoft.Office.Interop.VisOcx.AxDrawingControl drawingControl,
            double visioX,
            double visioY)
        {

            // The drawing control object must be valid.
            if (drawingControl == null)
            {

                // Throw a meaningful error.
                throw new ArgumentNullException("drawingControl");
            }

            int windowsX = 0;
            int windowsY = 0;
            double visioLeft;
            double visioTop;
            double visioWidth;
            double visioHeight;
            int pixelLeft;
            int pixelTop;
            int pixelWidth;
            int pixelHeight;
            Window referenceWindow;

            referenceWindow = (Window)drawingControl.Window;
            if (referenceWindow == null)
            {
                return System.Drawing.Point.Empty;
            }
            // Get the window coordinates in Visio units.
            referenceWindow.GetViewRect(out visioLeft, out visioTop,
                out visioWidth, out visioHeight);

            // Get the window coordinates in pixels.
            referenceWindow.GetWindowRect(out pixelLeft, out pixelTop,
                out pixelWidth, out pixelHeight);

            // Convert the X coordinate by using pixels per inch from the
            // width values.
            windowsX = (int)(pixelLeft +
                ((pixelWidth / visioWidth) * (visioX - visioLeft)));

            // Convert the Y coordinate by using pixels per inch from the
            // height values and transform from a top-left origin (windows
            // coordinates) to a bottom-left origin (Visio coordinates).
            windowsY = (int)(pixelTop +
                ((pixelHeight / visioHeight) * (visioTop - visioY)));

            return new System.Drawing.Point(windowsX, windowsY);            
        }

        [CLSCompliant(false)]
        public static System.Windows.Point MapWindowsToVisio(
            AxMicrosoft.Office.Interop.VisOcx.AxDrawingControl drawingControl,
            double x, double y)
        {
            if (drawingControl == null)
            {
                // Throw a meaningful error.
                throw new ArgumentNullException("drawingControl");
            }
            if (drawingControl.Window == null)
            {
                throw new Exception("The reference window cannot be null");
            }

            double visioX, visioY;
            double visioLeft;
            double visioTop;
            double visioWidth;
            double visioHeight;
            int pixelLeft, pixelTop, pixelWidth, pixelHeight;

            // Get the window coordinates in Visio units.
            drawingControl.Window.GetViewRect(out visioLeft, out visioTop,
                out visioWidth, out visioHeight);

            // Get the window coordinates in pixels.
            drawingControl.Window.GetWindowRect(out pixelLeft, out pixelTop,
                out pixelWidth, out pixelHeight);

            // Convert the X coordinate by using pixels per inch from the
            // width values.            
            visioX = visioLeft + ((x - pixelLeft) / (pixelWidth / visioWidth));

            // Convert the Y coordinate by using pixels per inch from the
            // height values and transform from a top-left origin (windows
            // coordinates) to a bottom-left origin (Visio coordinates).
            visioY = visioTop - ((y - pixelTop) / (pixelHeight / visioHeight));

            return new System.Windows.Point(visioX, visioY);
        }

        /// <summary>
        /// Conversione degli oggetti IPictureDisp (standard OLE) in immagini winforms o WPF.
        /// Usa metodi obsoleti, può girare solo su sistemi 32 bit. Bisogna capire se esiste un'alternativa
        /// </summary>
        /// <param name="pic"></param>
        /// <returns></returns>
        public static BitmapImage GetImageFromMasterPicture(IPictureDisp pic)
        {
            System.Drawing.Image img = Microsoft.VisualBasic.Compatibility.VB6.Support.IPictureDispToImage(pic);
            BitmapImage bi = new BitmapImage();
            bi.BeginInit();
            MemoryStream ms = new MemoryStream();
            img.Save(ms, ImageFormat.Png);
            ms.Seek(0, SeekOrigin.Begin);
            bi.StreamSource = ms;
            bi.EndInit();

            return bi;
        }

        /// <summary>
        /// Checks if a shape is fully contained in another shape
        /// </summary>
        /// <param name="PinLeftShape"></param>
        /// <param name="SizeLeftShape"></param>
        /// <param name="PinRightShape"></param>
        /// <param name="SizeRightShape"></param>
        /// <returns></returns>
        public static bool IsContained(WPF.Point PinLeftShape, WPF.Point SizeLeftShape, WPF.Point PinRightShape, WPF.Point SizeRightShape)
        {
            // RS is Right Shape, LS is Left Shape
            double LSRightX, LSTopY, LSBottomY, RSRightX, RSTopY, RSBottomY;

            // find the topleftmost coordinates and bottomrightmost coordinates of the left shape            
            LSRightX = PinLeftShape.X + SizeLeftShape.X / 2;
            LSBottomY = PinLeftShape.Y - SizeLeftShape.Y / 2;
            LSTopY = PinLeftShape.Y + SizeLeftShape.Y / 2;

            // find the topleftmost coordinates and bottomrightmost coordinates of the right shape            
            RSRightX = PinRightShape.X + SizeRightShape.X / 2;
            RSBottomY = PinRightShape.Y - SizeRightShape.Y / 2;
            RSTopY = PinRightShape.Y + SizeRightShape.Y / 2;

            return (LSRightX > RSRightX && LSTopY > RSTopY && LSBottomY < RSBottomY);

        }
    }    
}
